﻿using System.Collections.Generic;
using System.Linq;
using HappyBa.Core.Data;
using HappyBa.Core.Interfaces;

namespace HappyBa.Core.Statistics
{
    public class DevelopmentProgressStatistics : IStatisticsCalculator
    {
        public IEnumerable<string> Calculate(StatisticsContext context)
        {
            var list = new List<OvertimeInfo>();
            foreach (var project in context.Metrics.Projects)
            {
                foreach (var message in CalculateProject(project, context, list))
                {
                    yield return "[" + project + "]" + message;
                }
            }
            yield return string.Format("Overtime statistics: {0}", 
                string.Join(", ", list.GroupBy(x=>x.Developer).OrderBy(x=>x.Sum(s=>s.Overtime)).Select(x=>string.Format("{0} - {1:.0} ({2:0})", x.Key, x.Sum(s=>s.Overtime), x.Count()) )));
        }

        private static IEnumerable<string> CalculateProject(UIProject project, StatisticsContext context, List<OvertimeInfo> list)
        {
            var tasksStatistics = SumStatistics(project.UserStories.SelectMany(x => x.Tasks), context, list);
            var bugsStatistics = SumStatistics(project.Bugs.Concat(project.UserStories.SelectMany(u => u.Bugs)), context);
            return FormatMessage(tasksStatistics, bugsStatistics);
        }

        private static Statistics SumStatistics(IEnumerable<UIEstimatableEntityBase> items, StatisticsContext context, List<OvertimeInfo> list = null)
        {
            var s = new Statistics();
            foreach (var item in items)
            {
                CalcMetrics(item, s, context, list);
            }
            return s;
        }

        private static IEnumerable<string> FormatMessage(Statistics tasksStatistics, Statistics bugsStatistics)
        {
            decimal effort = tasksStatistics.EffortTime;
            if (effort == 0) effort = (decimal)0.000001;
            yield return string.Format("Tasks done: {0:0}, from: {1:0}, spentTime: {2:.0}, effort: {3:.0}, total time: {4:.0}. Defects fixed: {5:0}, from: {6:0}, time:{7:.0}({8:.0}%)",
                tasksStatistics.ActualDone, 
                tasksStatistics.TotalToDo,
                tasksStatistics.TimeSpent + bugsStatistics.TimeSpent, 
                tasksStatistics.EffortTime, 
                tasksStatistics.TotalTime,
                bugsStatistics.ActualDone,
                bugsStatistics.TotalToDo,
                bugsStatistics.TimeSpent,
                bugsStatistics.TimeSpent / effort * 100
                );
            yield return string.Format("* Expected overtime: {0:.0} ({1:.0}%), with bugfixing: {2:.0} ({3:.0}%) * , Overtimed tasks: {4}",
                tasksStatistics.TimeSpent - tasksStatistics.EffortTime,
                tasksStatistics.TimeSpent / effort * 100,
                (tasksStatistics.TimeSpent + bugsStatistics.TimeSpent) - tasksStatistics.EffortTime,
                (tasksStatistics.TimeSpent + bugsStatistics.TimeSpent) / effort * 100,
                string.Join(", ", tasksStatistics.Overtimed)
                );
        }

        private static void CalcMetrics(UIEstimatableEntityBase task, Statistics s, StatisticsContext context, List<OvertimeInfo> overtimedList)
        {
            if (task.Assignments.Any() &&
                !task.Assignments.Any(x => context.ActiveEmployees.Contains(x.GeneralUser.ToString())))
            {
                return;
            }
            if (context.Metrics.IsInProgress(task) || context.Metrics.IsFinished(task))
            {
                s.EffortTime += task.Effort;
                s.TimeSpent += task.TimeSpent;
                s.ActualDone++;
                if (task.TimeSpent > task.Effort && task.Effort > 0)
                {
                    var devName = string.Empty;
                    var dev = task.Assignments.FirstOrDefault(x => context.Metrics.IsDeveloper(x.GeneralUser));
                    if(dev !=null)
                        devName = dev.GeneralUser.ToString();
                    s.Overtimed.Add(string.Format("{0} by {1} [{2:.0}/{3:.0}]", task.Id, devName, task.TimeSpent, task.Effort));
                    if (overtimedList != null)
                    {
                        overtimedList.Add(new OvertimeInfo { Developer = devName, Overtime = task.TimeSpent - task.Effort });
                    }
                }
            }
            s.TotalToDo++;
            s.TotalTime += task.Effort;
        }

        class Statistics
        {
            public decimal EffortTime { get; set; }
            public decimal TimeSpent { get; set; }
            public decimal TotalTime { get; set; }
            public decimal TotalToDo { get; set; }
            public decimal ActualDone { get; set; }
            public IList<string> Overtimed { get; set; } 

            public Statistics()
            {
                EffortTime = TimeSpent = TotalTime = TotalToDo = ActualDone = 0;
                Overtimed = new List<string>();
            }
        }

        class OvertimeInfo
        {
            public string Developer { get; set; }
            public decimal Overtime { get; set; }
        }

    }
}
